/*
 * File     : CallingConvention.java
 * Created  : 1 May 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni;

import java.util.Collections;
import java.util.Set;

import com.googlecode.dni.internal.DniInternal;


/**
 * <p>
 *  Represents the calling conventions for native function calls.
 * </p>
 * <p>
 *  Calling conventions can also modify the name of the function (termed
 *  "mangling").
 * </p>
 *
 * @author Matthew Wilson
 */
public enum CallingConvention
{

    /** CDecl, also known as Sys-V. */
    X86_CDECL_UNIX,

    /** CDecl on Windows. */
    X86_CDECL_WINDOWS,

    /** Standard C call. */
    X86_STDCALL,

    // TODO X86_STDCALL_UNIX?

    /** Microsoft Windows x64 call. */
    X86_64_WINDOWS,

    /** Sys-V x86_64 call. */
    X86_64_UNIX,

    /**
     * Use the CDecl variant for the current platform.  On Windows x86, this
     * will be {@link #X86_CDECL_WINDOWS}.  On Linux x86, this will be
     * {@link #X86_CDECL_UNIX}.  Otherwise, it will be default calling
     * convention for that platform.
     */
    CDECL,

    /**
     * Use the stdcall variant for the current platform.  On Windows x86, this
     * will be {@link #X86_STDCALL}.  Otherwise, it will be default calling
     * convention for that platform.
     */
    STDCALL,

    /** Use the platform's default conventions. */
    PLATFORM_DEFAULT;

    private static Set< CallingConvention > supported = null;


    /**
     * <p>
     *  Determines the default conventions for the platform.  This value is used
     *  where {@link #PLATFORM_DEFAULT} is specified.
     * </p>
     *
     * @return the default conventions; guaranteed not to be
     *         {@link #PLATFORM_DEFAULT}
     */
    public static CallingConvention getDefault()
    {
        return DniInternal.PLATFORM.getDefaultCallingConvention();
    }

    /**
     * <p>
     *  Determines whether this calling convention is supported on this
     *  platform.
     * </p>
     *
     * @return <code>true</code> iff supported
     */
    public boolean isSupported()
    {
        Set< CallingConvention > local = supported;
        if ( local == null )
        {
            local = DniInternal.PLATFORM.getSupportedCallingConventions();
            local.add( PLATFORM_DEFAULT );

            // next line uses the constructor of the 'unmodifiableSet' to cause
            // a happens-before relationship between all prior actions here and
            // reads of the 'supported' field
            supported = Collections.unmodifiableSet( local );
        }

        return local.contains( this );
    }

}
